<?php
namespace JokerCode;

use Exception;
use think\facade\Cache;

class JokerVerify
{
    //水印图片地址
    private static $bj_dif; //背景图目录
    private static $mark; //缺失水印图
    private static $y;
    private static $mark_w;
    private static $mark_h;
    private static $bj_w;
    private static $bj_h;
    private static $y_rand; //y轴是否随机获取
    private static $x_min; //x最少移动像素
    private static $y_min; //y最少移动像素
    private static $x; //计算得出的x
    private static $fault = 2; //验证x左右容错px

    public static function __init($config = [])
    {
        if($config){
            self::$bj_dif = $config['bj_dif'];
            self::$mark = $config['mark'];
            self::$y = $config['y'];
            self::$y_rand = $config['y_rand'];
            self::$x_min = $config['x_min'];
            self::$y_min = $config['y_min'];
        }else{
            self::$bj_dif = dirname(__FILE__).'/bg/';
            self::$mark = dirname(__FILE__).'/img/mark.png';
            self::$y = 20;
            self::$y_rand = false;
            self::$x_min = 10;
            self::$y_min = 10;
        }
        return new static;
    }

    //生成验证码
    public static function make($id = '')
    {
        try{
            //获取背景图片和水印图片
            $bj = self::getBj();
            //计算x和y
            self::getXY($bj);
            //设置加水印背景图
            $img = self::setMark($bj);
            //设置session或者缓存
            Cache::set($id,json_encode([
                'x' => self::$x,
                'y' => self::$y,
                'time' => time(),
                'num' => 10 //使用次数 用一次减一次
            ]),600);
            return [
                'img' => $img,
                'y' => self::$y
            ];
        }catch(Exception $e){
            throw new Exception($e->getMessage());
        }
    }

    //验证x
    public static function check($id,$x)
    {
        if(!$id)
            throw new Exception('id为空');
        $result = Cache::get($id);
        if(!$result)
            throw new Exception('验证码过期,请刷新重新获取');
        $result = json_decode($result,true);
        //计算容错
        if(abs($x - $result['x']) <= self::$fault)
            return true;
        //失败次数减一
        if($result['num'] <= 0){
            //次数到了删除缓存
            Cache::delete($id);
        }
        $result['num'] = $result['num'] - 1;
        Cache::set($id,json_encode($result),600);
        return false;
    }

    //设置水印背景图
    private static function setMark($bj)
    {
        //遮盖层
        $cover = imagecreatefrompng(self::$mark);
        //创建一个和遮盖层同样大小的图片
        $img = imagecreatetruecolor(self::$mark_w, self::$mark_h);
        imagesavealpha($img, true);
        $bg = imagecolorallocatealpha($img, 255, 0, 0, 127);
        imagefill($img, 0, 0, $bg);//背景层
        
        $background = imagecreatefromstring(file_get_contents($bj));
        
        $width_ini = self::$x;
        $height_ini = self::$y;
        
        $width_limit = $width_ini + self::$mark_w;
        $height_limit = $height_ini + self::$mark_h;
        
        for ($i=$width_ini; $i < $width_limit; $i++) { 
            for ($j=$height_ini; $j < $height_limit; $j++) { 
                $color2 = imagecolorat($background, $i, $j);
                //判断索引值区分具体的遮盖区域
                if(imagecolorat($cover, $i-$width_ini, $j-$height_ini) == 0){
                    imagesetpixel($img, $i-$width_ini, $j-$height_ini, $color2);
                }
                $color1 = imagecolorat($cover, $i-$width_ini, $j-$height_ini);
                $s = imagecolorallocatealpha($background, 100, 100, 100, 0);
                if($color1 == 0){
                    imagesetpixel($background,$i,$j,$s);
                }
            }
        }
        //生成背景图
        $new_bj_path = dirname(__FILE__).'/bg/new_bj.png';
        imagepng($background,$new_bj_path);
        //生成滑块图
        $new_bj_mark = dirname(__FILE__).'/bg/new_mark.png';
        imagepng($img,$new_bj_mark);
        imagedestroy($background);
        imagedestroy($img);
        //base64背景图
        $mime_type= mime_content_type($new_bj_path);
        $base64_data = base64_encode(file_get_contents($new_bj_path));
        $base64_background = 'data:'.$mime_type.';base64,'.$base64_data;
        $mime_type= mime_content_type($new_bj_mark);
        $base64_data = base64_encode(file_get_contents($new_bj_mark));
        $base64_mark = 'data:'.$mime_type.';base64,'.$base64_data;
        unlink($new_bj_path);
        unlink($new_bj_mark);
        return [
            'bj' => $base64_background,
            'mark' => $base64_mark
        ];
        // //读取二维码数据流
        // $bj_img = imagecreatefromstring(file_get_contents($bj));
        // $mark_img = imagecreatefromstring(file_get_contents(self::$mark));
        // imagecopymerge($bj_img, $mark_img, self::$x, self::$y , 0, 0, self::$mark_w, self::$mark_h, 20);
        // imagepng($bj_img,dirname(__FILE__).'/bg/9.png');
        // dump($bj_img);exit;
    }

    //随机选择一张背景图
    private static function getBj()
    {
        //随机选择一张背景图
        $path = self::$bj_dif;
        //判断目录是否为空
        if(!file_exists($path)) 
            throw new Exception('目录中没有背景图！');
        $files = scandir($path);
        //获取.和..的key
        $key1 = array_search('.',$files);
        $key2 = array_search('..',$files);
        if($key1 !== false)
            unset($files[$key1]);
        if($key2 !== false)
            unset($files[$key2]);
        $new_arr = array_values($files);
        //随机获取
        $bj_path = array_rand($new_arr);
        return $path.$new_arr[$bj_path];
    }

    //计算x和y
    private static function getXY($bj)
    {
        $mark = self::$mark;
        //获取水印和背景图片高宽
        if(!file_exists($bj))
            throw new Exception('背景图片不存在');
        if(!file_exists($mark))
            throw new Exception('水印图片不存在');
        //size:0是宽，1是高
        $bj_size = getimagesize($bj);
        $mark_size = getimagesize($mark);
        self::$bj_w = $bj_size[0];
        self::$bj_h = $bj_size[1];
        self::$mark_w = $mark_size[0];
        self::$mark_h = $mark_size[1];
        //计算最大x和最大y
        $max_x = $bj_size[0] - $mark_size[0];
        $max_y = $bj_size[1] - $mark_size[1];
        //判断最小移动是否大于总长度
        if(self::$x_min > $max_x)
            throw new Exception('x轴最小范围不应该大于可用范围');
        if(self::$y_min > $max_y)
            throw new Exception('y轴最小范围不应该大于可用范围');
        //y随机获取点位或者固定点位
        if(self::$y_rand){
            self::$y = mt_rand(0,$max_y);
        }
        //设置x和y轴点
        self::$y = self::$y <= $max_y ? self::$y : self::$y_min;
        self::$x = mt_rand(self::$x_min,$max_x);
    }
}